<?php
/**
 * This file is part of workerman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link http://www.workerman.net/
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */

/**
 * 用于检测业务代码死循环或者长时间阻塞等问题
 * 如果发现业务卡死，可以将下面declare打开（去掉//注释），并执行php start.php reload
 * 然后观察一段时间workerman.log看是否有process_timeout异常
 */
declare (ticks = 1);

/**
 * 主要处理 onMessage onClose
 */
use Workerman\Lib\Timer;
use \GatewayWorker\Lib\Gateway;

require_once '/home/ubuntu/webserver/workerman-tcp_server/vendor/workerman/mysql-master/src/Connection.php';
//require_once  '/home/ubuntu/webserver/workerman-tcp_server/vendor/autoload.php';
require_once 'util.php';

$serverSerials = 30;
$heartTimer = 0;
$db = null;

class Events
{
    public static function onWorkerStart($worker)
    {
        global $db;
        $db = new \Workerman\MySQL\Connection('127.0.0.1', 'port', 'root', 'li999888', 'module_db');
    }

    public static function onWorkerStop($worker)
    {
        //$db->close();
    }

    public static function onConnect($client_id)
    {
        myEcho("client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} onConnect");
    }
    /**
     * 有消息时
     * @param int $client_id
     * @param mixed $message
     */
    public static function onMessage($client_id, $message)
    {
        //echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']}  client_id:$client_id session:".json_encode($_SESSION)." onMessage:".$message."\n";

        myEcho("onMessage: client_id:{$client_id}");
        //Gateway::sendToClient($client_id, encodeMessage("7E007E"));
        //Events::processModuleMsg($client_id, $message);
        //return;
        $jsonMsg = json_decode($message, true);
        if (is_array($jsonMsg)) {
            Events::processWebMessage($client_id, $jsonMsg);
        } else {
            Events::processModuleMsg($client_id, $message);
        }
    }

    /**
     * 当客户端断开连接时
     * @param integer $client_id 客户端id
     */
    public static function onClose($client_id)
    {
        global $heartTimer;
        if ($heartTimer) {
            Timer::del($heartTimer);
        }
        myEcho("client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} onClose");
    }

    public static function processWebMessage($client_id, $message)
    {
        global $db;

        echo "processWebMessage:";
        print_r($message);
        $devId = $message['devId'];
        switch ($message['type']) {
            case 'oilOff':
            case 'oilOn':
            case 'reboot':
            case 'restore':
            case 'bf':
            case 'cf':
            case 'gpsOff':
            case 'verno':
            case 'iccid':
                if (Gateway::isUidOnline($devId)) {
                    $module_client_id = Gateway::getClientIdByUid($devId);
                    //echo "module_client_id:";
                    //print_r($module_client_id);
                    if (count($module_client_id) > 0) {
                        //var_dump(ModuleConst::$moduleCtrlCmd);
                        $module_client_id = $module_client_id[0];
                        $module_session = Gateway::getSession($module_client_id);
                        $serverSerials = $module_session['serverSerials'];
                        echo "\n", $message['type'], ' ', ModuleConst::$moduleCtrlCmd[$message['type']], "\n";
                        $sendData = sprintf("%s%04X%s%04X%02X", '8105', 1, $devId, $serverSerials++, ModuleConst::$moduleCtrlCmd[$message['type']]);
                        Events::packAndSendModuleMsg($module_client_id, $sendData);
                        Gateway::updateSession($module_client_id, array('serverSerials' => $serverSerials));
                        Gateway::updateSession($module_client_id, array('webClientId' => $client_id, 'type' => $message['type']));
                        //Gateway::sendToClient($client_id, '{"type":"oilOff", "result": "true"}');
                        return;
                    }
                }
                Gateway::sendToClient($client_id, json_encode(array('type' => $message['type'], 'result' => 'false')));
                break;
            case 'pong':
                break;
            case 'location':
                $ret = $db->select('latitude,longitude')->from('location_info')->where("dev_id={$devId}")->orderByDESC(array('gps_dt'))->query();
                Gateway::sendToClient($client_id, json_encode(array('type' => 'location', 'lat' => $ret[0]['latitude'], 'lon' => $ret[0]['longitude'])));
                break;
            case 'login':
                Gateway::sendToClient($client_id, '{"type":"login", "client_name":"' . $devId . '"}');
                break;
            default:
                Gateway::sendToClient($client_id, '{"type":"common"}');
                break;
        }
    }

    public static function generateJQM($devId)
    {
        $jqm = '';
        $len = strlen($devId);
        $i = 0;
        while ($i < $len) {
            $jqm .= sprintf("%02X", ord($devId[$i++]));
        }
        return $jqm;
    }

    public static function ping($client_id)
    {
        $devId = Gateway::getUidByClientId($client_id);
        myEcho("ping out: $devId");
        if ($client_id) {
            Gateway::closeClient($client_id);
        }
    }

    public static function packAndSendModuleMsg($client_id, $message)
    {
        global $db;

        $devId = Gateway::getUidByClientId($client_id);
        $sendCheckSum = checkSum($message);
        $message = sprintf("7E%s%02X7E", $message, $sendCheckSum);
        $message = strtoupper($message);
        myEcho("send:$message");

        $db->insert('original_data')->cols(array('dev_id' => $devId, 'is_client' => '0', 'data' => $message))->query();
        $message = encodeMessage($message);
        Gateway::sendToClient($client_id, $message);
    }

    public static function processModuleMsg($client_id, $message)
    {
        global $serverSerials;
        global $heartTimer;
        global $db;

        myEcho("recv");
        $t1 = microtime(true);

        $message = decodeMessage($message);
        $msg = bin2hex($message);
        $msg = strtoupper($msg);
        //myEcho("recv:$msg");

        $cmd = substr($msg, 2, 4);
        $msgLen = substr($msg, 6, 4);
        $msgLen = hexdec($msgLen);
        $devId = substr($msg, 10, 12);
        $serial = substr($msg, 22, 4);
        $data = substr($msg, 26, $msgLen * 2);
        $checkSum = substr($msg, 26 + $msgLen * 2, 2);

        $calcCheckSum = substr($msg, 2, strlen($msg) - 6);
        $calcCheckSum = checkSum($calcCheckSum);
        if (ord(hex2bin($checkSum)) != $calcCheckSum) {
            myEcho("Error: CheckSum Wrong!");
            Gateway::sendToClient($client_id, 0);
            return;
        }

        /*心跳处理*/
        if ($heartTimer) {
            Timer::del($heartTimer);
        }
        $heartTimer = Timer::add(130, function ($client_id) {
            $devId = Gateway::getUidByClientId($client_id);
            myEcho("ping out: client_id: $client_id   devId: $devId");
            Gateway::closeClient($client_id);
        }, array($client_id), false);

        $db->insert('original_data')->cols(array('dev_id' => $devId, 'is_client' => '1', 'data' => $msg))->query();

        //echo "$devId--isUidOnline:", Gateway::isUidOnline($devId), "\n";
        switch ($cmd) {
            case '0100': //register
                $sendCmd = '8100';
                if (isset($_SESSION['devId']) && $_SESSION['devId'] == $devId) {
                    myEcho("device: $devId already online!!!!");
                    $sendData = sprintf("%s%04X%s%04X%s%02X", $sendCmd, 3, $devId, $serverSerials++, $serial, 1);
                    break;
                }
                $sendJQM = Events::generateJQM($devId);
                $_SESSION['devId'] = $devId;
                $sendLen = 3 + strlen($sendJQM) / 2;
                $sendData = sprintf("%s%04X%s%04X%s%02X%s", $sendCmd, $sendLen, $devId, $serverSerials++, $serial, 0, $sendJQM);
                break;
            case '0001': //common rsp
                $clientSerials = substr($data, 0, 4);
                $clientCmd = substr($data, 4, 4);
                $clientResult = substr($data, 8, 2);
                if ($clientCmd == '8105') {
                    $rspMsg = array();
                    $rspMsg['result'] = 'true';
                    $rspMsg['type'] = $_SESSION['type'];
                    Gateway::sendToClient($_SESSION['webClientId'], json_encode($rspMsg));
                }
                return;
            case '8881':
                $rspMsg = array();
                $rspMsg['result'] = 'true';
                $rspMsg['type'] = $_SESSION['type'];
                $rspMsg['data'] = hex2bin($data);
                Gateway::sendToClient($_SESSION['webClientId'], json_encode($rspMsg));
                return;
            case '0002': //heart
            case '0102': //jq
            case '0200': //location
            default:
                $sendCmd = '8001';
                $sendLen = 5;
                $result = 0;
                if ($cmd == '0102') {
                    if (Events::generateJQM($devId) != $data || Gateway::isUidOnline($devId)) {
                        $result = 1;
                    } else {
                        Gateway::bindUid($client_id, $devId);
                    }
                } else if ($cmd == '0200') {
                    Events::parseLocationData($devId, $data);
                }
                $sendData = sprintf("%s%04X%s%04X%s%s%02X", $sendCmd, $sendLen, $devId, $serverSerials++, $serial, $cmd, $result);
                break;
        }
        $_SESSION['serverSerials'] = $serverSerials;
        Events::packAndSendModuleMsg($client_id, $sendData);
        $t2 = microtime(true);
        echo "run time:", $t2 - $t1, "\n\n";
    }

    public static function parseLocationData($devId, $data)
    {
        global $db;
        $alarm = hexdec(substr($data, 0, 8));
        $state = hexdec(substr($data, 8, 8));
        $latitude = hexdec(substr($data, 16, 8));
        $longitude = hexdec(substr($data, 24, 8));
        $altitude = hexdec(substr($data, 32, 4));
        $speed = hexdec(substr($data, 36, 4));
        $direction = hexdec(substr($data, 40, 4));
        $gps_dt = substr($data, 44, 12);
        $gps_dt = substr($gps_dt, 0, 2) . '-' . substr($gps_dt, 2, 2) . '-' . substr($gps_dt, 4, 2) . ' ' . substr($gps_dt, 6, 2) . ':' . substr($gps_dt, 8, 2) . ':' . substr($gps_dt, 10, 2);
        $addition = substr($data, 56, strlen($data) - 56);
        myEcho("{$alarm}  {$state}  {$latitude}  {$longitude}  {$altitude}  {$speed}  {$direction}  {$gps_dt}  {$addition}");
        $db->insert('location_info')
            ->cols(array('dev_id' => $devId,
                'alarm' => $alarm,
                'state' => $state,
                'latitude' => $latitude,
                'longitude' => $longitude,
                'altitude' => $altitude,
                'speed' => $speed,
                'gps_dt' => $gps_dt,
                'addition' => $addition,
                'outer_battery_volt' => 0,
                'inner_battery_volt' => 0))
            ->query();
    }
}

function myEcho($msg)
{
    echo date("Y-m-d H:i:s  "), $msg, "\n";
}

class ModuleConst
{
    public static $moduleCtrlCmd = array(
        'oilOff' => 0x64,
        'oilOn' => 0x65,
        'reboot' => 0x04,
        'restore' => 0x05,
        'bf' => 0xA0,
        'cf' => 0xA1,
        'gpsOff' => 0x82,
        'verno' => 0x84,
        'iccid' => 0x85,
    );
}
